/*
    SDL - Simple DirectMedia Layer
    Partial implementation of SDL library (originally written by
    Sam Lantinga <slouken@libsdl.org>) for the particular purpose to support
    Prequengine (http://code.google.com/p/prequengine/) on BlackBerry(tm)
    tablets and smartphones.

    Copyright (C) 2013  xlamsp

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

    xlamsp@gmail.com
*/

#include "SDL_events.h"
#include "SDL_sysvideo.h"

/* Private data -- event queue */
#define MAXEVENTS       128
static struct {
    int active;
    int head;
    int tail;
    SDL_Event event[MAXEVENTS];
} SDL_EventQ;

void SDL_StopEventLoop(void)
{
    SDL_Log(DEBUG1, "entered");

    /* Clean out EventQ */
    SDL_EventQ.active = 0;
    SDL_EventQ.head = 0;
    SDL_EventQ.tail = 0;
}

int SDL_StartEventLoop(Uint32 flags)
{
    SDL_Log(DEBUG1, "entered");

    /* Clean out EventQ */
    SDL_EventQ.head = 0;
    SDL_EventQ.tail = 0;
    SDL_EventQ.active = 1;

    return 0;
}

/* Add an event to the event queue */
static int SDL_AddEvent(SDL_Event *event)
{
    int tail, added;

    tail = (SDL_EventQ.tail + 1) % MAXEVENTS;

    if(tail == SDL_EventQ.head) {
        /* Overflow, drop event */
        SDL_Log(WARNING, "dropped event %u", (Uint32)(event->type));
        added = 0;
    } else {
        SDL_EventQ.event[SDL_EventQ.tail] = *event;
        SDL_EventQ.tail = tail;
        added = 1;
    }

    return (added);
}

/* Cut an event, and return the next valid spot, or the tail */
static int SDL_CutEvent(int spot)
{
    if (spot == SDL_EventQ.head) {
        SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
        return (SDL_EventQ.head);
    } else if((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
        SDL_EventQ.tail = spot;
        return (SDL_EventQ.tail);
    } else {
        /* We cut the middle -- shift everything over */
        int here, next;

        /* This can probably be optimized with SDL_memcpy() -- careful! */
        if(--SDL_EventQ.tail < 0) {
            SDL_EventQ.tail = MAXEVENTS - 1;
        }

        for(here=spot; here != SDL_EventQ.tail; here = next) {
            next = (here + 1) % MAXEVENTS;
            SDL_EventQ.event[here] = SDL_EventQ.event[next];
        }

        return (spot);
    }
    /* NOTREACHED */
}

void SDL_PumpEvents(void)
{
    SDL_VideoDevice *video = current_video;

    /* Get events from the video subsystem */
    if(video) {
        video->PumpEvents(video);
    }
}

int SDL_PeepEvents(SDL_Event *events, int numevents,
                        SDL_eventaction action, Uint32 mask)
{
    int i, used;

    /* Don't look after we've quit */
    if(!SDL_EventQ.active) {
        return (-1);
    }

    used = 0;

    if(action == SDL_ADDEVENT) {
        for(i = 0; i < numevents; ++i) {
            used += SDL_AddEvent(&events[i]);
        }
    } else {
        SDL_Event tmpevent;
        int spot;

        /* If 'events' is NULL, just see if they exist */
        if(events == NULL) {
            action = SDL_PEEKEVENT;
            numevents = 1;
            events = &tmpevent;
        }

        spot = SDL_EventQ.head;
        while((used < numevents) && (spot != SDL_EventQ.tail)) {
            if(mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type)) {
                events[used++] = SDL_EventQ.event[spot];
                if(action == SDL_GETEVENT) {
                    spot = SDL_CutEvent(spot);
                } else {
                    spot = (spot + 1) % MAXEVENTS;
                }
            } else {
                spot = (spot + 1) % MAXEVENTS;
            }
        }
    }

    return (used);
}

int SDL_PollEvent(SDL_Event *event)
{
    SDL_PumpEvents();

    /* We can't return -1, just return 0 (no event) on error */
    if(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0) {
        return 0;
    }

    return 1;
}

int SDL_PushEvent(SDL_Event *event)
{
    if(SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0) {
        return -1;
    }

    return 0;
}

